/*
 * @(#)RegistryElasticsearchService.java
 * Date 2013-04-08
 * Version 1.0
 * Author Jim Horner
 * Copyright (c)2012
 */


package us.hornerscorners.lamppost.registry.service.es;

import javax.enterprise.context.ApplicationScoped;

import javax.enterprise.inject.Alternative;

import javax.inject.Inject;

import oasis.names.tc.ebxml_regrep.xsd.lcm._3.SubmitObjectsRequest;

import oasis.names.tc.ebxml_regrep.xsd.rs._3.RegistryResponseType;

import org.apache.commons.lang3.ArrayUtils;

import org.elasticsearch.action.bulk.BulkResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import us.hornerscorners.lamppost.exception.ServiceActionException;

import us.hornerscorners.lamppost.ihecommon.service.ElasticSearchSource;

import us.hornerscorners.lamppost.model.audit.AuditContext;

import us.hornerscorners.lamppost.model.document.DocumentEntry;
import us.hornerscorners.lamppost.model.document.Folder;
import us.hornerscorners.lamppost.model.document.SubmissionSet;

import us.hornerscorners.lamppost.model.patient.PatientIdentity;

import us.hornerscorners.lamppost.registry.constant.AvailabilityStatus;
import us.hornerscorners.lamppost.registry.constant.ErrorCode;
import us.hornerscorners.lamppost.registry.constant.ErrorSeverityType;
import us.hornerscorners.lamppost.registry.constant.ResponseStatus;

import us.hornerscorners.lamppost.registry.inject.RegistryTarget;

import us.hornerscorners.lamppost.registry.model.RegistryObjects;
import us.hornerscorners.lamppost.registry.model.RegistryResponse;

import us.hornerscorners.lamppost.registry.service.AtnaRegistryService;
import us.hornerscorners.lamppost.registry.service.IRegistryPersistService;

import us.hornerscorners.lamppost.registry.transformer.model.RegistryObjectsTransformer;

import us.hornerscorners.lamppost.registry.transformer.xds
    .RegistryResponseTypeTransformer;

import us.hornerscorners.lamppost.registry.utils.XDSUtils;

import us.hornerscorners.lamppost.utils.UTCDateUtils;


/**
 *
 * @author jim
 */
@ApplicationScoped
@Alternative
public class RegistryPersistService implements IRegistryPersistService {

    /** Field description */
    @Inject
    private AtnaRegistryService atnaService;

    /** Field description */
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** Field description */
    @Inject
    private RegistryObjectsTransformer registryObjectsTransformer;

    /** Field description */
    @Inject
    @RegistryTarget
    private RegistryResponseTypeTransformer registryResponseTransformer;

    /** Field description */
    @Inject
    private ElasticSearchSource source;

    /**
     * Constructs ...
     *
     *
     */
    public RegistryPersistService() {

        super();
    }

    /**
     * Method description
     *
     *
     * @param rol
     *
     */
    private void prepareRegistryMetadata(RegistryObjects rol)
            throws ServiceActionException {

        String approveStatus = AvailabilityStatus.Approved.getCode();
        String submittedStatus = AvailabilityStatus.Submitted.getCode();

        for (DocumentEntry docentry : rol.getDocumentEntries()) {

            if (XDSUtils.isInStatus(docentry, submittedStatus)) {

                docentry.getAvailabilityStatus().setCode(approveStatus);
            }
        }

        for (Folder folder : rol.getFolders()) {

            folder.setLastUpdateTime(UTCDateUtils.now());

            if (XDSUtils.isInStatus(folder, submittedStatus)) {

                folder.getAvailabilityStatus().setCode(approveStatus);
            }
        }

        for (SubmissionSet subset : rol.getSubmissionSets()) {

            if (XDSUtils.isInStatus(subset, submittedStatus)) {

                subset.getAvailabilityStatus().setCode(approveStatus);
            }
        }

    }

    /**
     * Method description
     *
     *
     * @param body
     *
     * @return
     *
     */
    @Override
    public RegistryResponseType registerDocumentSet(AuditContext auditContext,
            SubmitObjectsRequest body) {

        RegistryResponseType result = null;

        RegistryObjects rol = null;

        try {

            rol = this.registryObjectsTransformer.transform(body);

        } catch (ServiceActionException e) {

            logger.error(e.getMessage(), e);
            result = this.registryResponseTransformer.transform(
                new RegistryResponse(ResponseStatus.Failure, e));

            sendRegisterAuditMessage(auditContext, rol, result);
        }

        if (result == null) {

            try {

                prepareRegistryMetadata(rol);

                BulkResponse bulkWriteResponse = writeRegistryMetadata(rol);

                if (bulkWriteResponse.hasFailures()) {

                    this.source.rollbackTransaction(bulkWriteResponse);

                    result = this.registryResponseTransformer.transform(
                        ResponseStatus.Failure, getClass(), ErrorCode.XDSRegistryError,
                        bulkWriteResponse.buildFailureMessage(), ErrorSeverityType.Error);

                } else {

                    result = this.registryResponseTransformer.transform(
                        new RegistryResponse(ResponseStatus.Success));
                }

                this.source.flush();

            } catch (ServiceActionException e) {

                logger.error(e.getMessage(), e);
                result = this.registryResponseTransformer.transform(
                    new RegistryResponse(ResponseStatus.Failure, e));

            } finally {

                sendRegisterAuditMessage(auditContext, rol, result);
            }
        }

        return result;
    }

    /**
     * Method description
     *
     *
     * @param auditContext
     * @param rol
     * @param result
     */
    private void sendRegisterAuditMessage(AuditContext auditContext, RegistryObjects rol,
            RegistryResponseType result) {

        PatientIdentity patientIdentity = null;
        SubmissionSet submissionSet = null;

        if (rol != null) {

            patientIdentity = rol.getPatientIdentity();

            SubmissionSet[] subSets = rol.getSubmissionSets();

            if (ArrayUtils.isNotEmpty(subSets)) {

                submissionSet = subSets[0];
            }
        }

        this.atnaService.sendRegisterReceivedAuditMessage(auditContext, patientIdentity,
                submissionSet, result);
    }

    /**
     * Method description
     *
     *
     * @param source
     */
    protected void setSource(ElasticSearchSource source) {
        this.source = source;
    }

    /**
     * Method description
     *
     *
     * @param rol
     *
     * @return
     *
     * @throws ServiceActionException
     */
    protected BulkResponse writeRegistryMetadata(RegistryObjects rol)
            throws ServiceActionException {

        return this.source.writePersistables(rol.getAllPersistables());
    }
}
